#include "timeutil.h"

#include <boost/date_time/posix_time/posix_time.hpp>

namespace timeutil
{

DateTime DateTime::now() {
    const boost::posix_time::ptime t = boost::posix_time::microsec_clock::local_time();
    const auto date = t.date();
    const Date::Year year = static_cast<Date::Year>(date.year());
    const Date::Month month = static_cast<Date::Month>(date.month().as_number());
    const Date::Day day = static_cast<Date::Day>(date.day().as_number());
    const auto timeInDay = t.time_of_day();
    const TimeInDay::Hour h = static_cast<TimeInDay::Hour>(timeInDay.hours());
    const TimeInDay::Minute m = static_cast<TimeInDay::Minute>(timeInDay.minutes());
    const TimeInDay::Second s = static_cast<TimeInDay::Second>(timeInDay.seconds());
    const TimeInDay::MilliSecond ms = static_cast<TimeInDay::MilliSecond>(
        timeInDay.total_milliseconds() -
        ((static_cast<int64_t>(h) * 3600 + static_cast<int64_t>(m) * 60 + static_cast<int64_t>(s)) * 1000));

    return DateTime(Date(year, month, day), TimeInDay(h, m, s, ms));
}

template <typename N>
static inline void printMonth(std::ostream &os, N month) {
    if (month < 10) {
        os << '0';
    }
    os << month;
}

template <typename N>
static inline void printDayInMonth(std::ostream &os, N day) {
    if (day < 10) {
        os << '0';
    }
    os << day;
}

template <typename N>
static inline void printHour(std::ostream &os, N hour) {
    if (hour < 10) {
        os << '0';
    }
    os << hour;
}

template <typename N>
static inline void printMin(std::ostream &os, N min) {
    if (min < 10) {
        os << '0';
    }
    os << min;
}

template <typename N>
static inline void printSec(std::ostream &os, N sec) {
    if (sec < 10) {
        os << '0';
    }
    os << sec;
}

template <typename N>
static inline void printMilliSec(std::ostream &os, N milli) {
    if (milli < 10) {
        os << '0';
    }
    if (milli < 100) {
        os << '0';
    }
    os << milli;
}

void DateTime::print(std::ostream &out, const PrintOpt &opt) const {
    out << date_.y();
    out << opt.dateSep();
    printMonth(out, date_.m());
    out << opt.dateSep();
    printDayInMonth(out, date_.d());
    out << opt.dateTimeSep();
    printHour(out, time_.h());
    out << opt.timeSep();
    printMin(out, time_.m());
    out << opt.timeSep();
    printSec(out, time_.m());
    out << opt.milliSecSep();
    printMilliSec(out, time_.ms());
}

static const DateTime::PrintOpt kDefaultDateTimePrintOpt;

void DateTime::print(std::ostream &out) const {
    print(out, kDefaultDateTimePrintOpt);
}

} // namespace timeutil
